package com.mapforge.util;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.HashSet;

import javax.imageio.ImageIO;

public final class ImageUtil {
	private static final int TRANSPARENT_ARGB = new Color(0, 0, 0, 0).getRGB();

	/**
	 * A privately defined constructor. Since this class is purely a utility
	 * class there should never be a need for an instance of it to exist.
	 */
	private ImageUtil() {
	}

	public static BufferedImage getImage(URL input) throws IOException {
		return ImageIO.read(input);
	}

	public static BufferedImage copyImage(BufferedImage image) {
		BufferedImage copyImg = new BufferedImage(image.getWidth(), image
				.getHeight(), image.getTransparency());
		copyImg.getGraphics().drawImage(image, 0, 0, null);
		return copyImg;
	}

	public static void applyMask(BufferedImage img, Color... colors) {
		int w = img.getWidth();
		int h = img.getHeight();
		int[] pixels = new int[w * h];
		img.getRGB(0, 0, w, h, pixels, 0, w);
		HashSet<Integer> hashset = new HashSet<Integer>();
		for (int i = 0; i < colors.length; i++)
			hashset.add(colors[i].getRGB());

		for (int i = 0; i < pixels.length; i++) {
			if (hashset.contains(pixels[i]))
				pixels[i] = TRANSPARENT_ARGB;
		}
	}

	public static BufferedImage createImage(int w, int h, int transparency) {
		if (transparency == Transparency.BITMASK
				|| transparency == Transparency.OPAQUE
				|| transparency == Transparency.TRANSLUCENT)
			return new BufferedImage(w, h, transparency);
		else
			throw new IllegalArgumentException(
					"Unrecognized type of transparency.");
	}

	/**
	 * Returns whether the specified {@link BufferedImage} has pixels who's
	 * alpha value
	 * 
	 * @param img
	 * @return
	 */
	public static boolean hasTranslucentPixels(BufferedImage img) {
		return hasTranslucentPixels(img, 0, 0, img.getWidth(), img.getHeight());
	}

	public static boolean hasTranslucentPixels(BufferedImage img, int x, int y,
			int width, int height) {
		int val;
		for (int y2 = y; y2 < height; y2++) {
			for (int x2 = x; x2 < width; x2++) {
				int pixel = img.getRGB(x2, y2);
				pixel = ColorUtil.getAlpha(pixel);
				val = (pixel & 0xFF);
				// if not entirely opaque or translucent
				if (val != 0xFF && val != 0) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Calculates the transparency of a subsection of a {@link BufferedImage} by
	 * checking the alpha value of each pixel contained within the rectangle
	 * specified by <code>(x, y, width, height)</code> variables.
	 * 
	 * @param img
	 *            {@link BufferedImage} which will be scanned
	 * @param x
	 *            specified x value of area to be scanned
	 * @param y
	 *            specified y value of area to be scanned
	 * @param width
	 *            specified width of area to be scanned
	 * @param height
	 *            specified height of area to be scanned
	 * @return
	 */
	public static int getTransparency(BufferedImage img, int x, int y,
			int width, int height) {
		int transparency = Transparency.OPAQUE;
		for (int y2 = y; y2 < height; y2++) {
			for (int x2 = x; x2 < width; x2++) {
				int pixel = ColorUtil.getAlpha(img.getRGB(x2, y2)) & 0xFF;
				if (transparency == Transparency.OPAQUE) {
					// value might be bitmask
					if (pixel == 0) {
						transparency = Transparency.BITMASK;
					} else if (pixel != 0xFF) {
						// only left is between 1-254, must be translucent
						return Transparency.TRANSLUCENT;
					}
				}
				// have encountered 0 alpha value previously
				else if (transparency == Transparency.BITMASK) {
					// must be translucent
					if (pixel != 0xFF && pixel != 0) {
						return Transparency.TRANSLUCENT;
					}
				}
			}
		}
		// scanned full area, return result
		return transparency;
	}

	public static BufferedImage[] splitImages(BufferedImage img, int tileW,
			int tileH) throws IllegalArgumentException {
		if (img.getWidth() < tileW || img.getHeight() < tileH) {
			throw new IllegalArgumentException(
					"Tile dimensions must be greater than image dimensions.");
		}
		int imgW = img.getWidth();
		int w = imgW / tileW;
		int h = img.getHeight() / tileH;
		int x = 0;
		int y = 0;
		BufferedImage[] imgs = new BufferedImage[w * h];
		GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment()
				.getDefaultScreenDevice();
		GraphicsConfiguration gc = gd.getDefaultConfiguration();

		boolean istransparent = img.getColorModel().hasAlpha();
		if (istransparent) {
			// is transparency of subimage bitmask, opaque, or translucent?
			for (int i = 0; i < imgs.length; i++) {
				imgs[i] = gc.createCompatibleImage(tileW, tileH,
						getTransparency(img, x, y, tileW, tileH));
				if (x >= imgW) {
					y += tileH;
					x = 0;
				}
			}
		} else {

		}
		Graphics2D g;
		int index = 0;
		w *= tileW;
		h *= tileH;

		for (y = 0; y < h; y += tileH) {
			for (x = 0; x < w; x += tileW) {
				g = imgs[index].createGraphics();
				g.drawImage(img, 0, 0, tileW, tileH, x, y, x + tileW,
						y + tileH, null);
				g.dispose();
				++index;
			}
		}
		return imgs;
	}

	public static BufferedImage optimizeImage(BufferedImage img, int dx1,
			int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2) {
		GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment()
				.getDefaultScreenDevice();
		GraphicsConfiguration gc = gd.getDefaultConfiguration();

		boolean istransparent = img.getColorModel().hasAlpha();

		BufferedImage img2 = gc.createCompatibleImage(dx2, dy2,
				istransparent ? Transparency.BITMASK : Transparency.OPAQUE);
		Graphics2D g = img2.createGraphics();
		g.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
		g.dispose();

		return img2;
	}
}
